package com.qf.business.redevnlopes.core.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qf.business.redevnlopes.core.dao.RedEnvelopesDao;
import com.qf.business.redevnlopes.core.service.RedEnvelopesDetailsService;
import com.qf.business.redevnlopes.core.service.RedEnvelopesService;
import com.qf.business.user.feign.WxUserFeign;
import com.qf.commons.core.exception.ServiceException;
import com.qf.commons.core.utils.QfBeanUtils;
import com.qf.commons.redis.annotation.KLock;
import com.qf.commons.web.aspect.annotation.GetUser;
import com.qf.commons.web.utils.UserUtils;
import com.qf.data.redenvlopes.dto.RedEnvelopesDto;
import com.qf.data.redenvlopes.entity.RedEnvelopes;
import com.qf.data.redenvlopes.entity.RedEnvelopesDetails;
import com.qf.data.redenvlopes.vo.input.RedEvnlopesInput;
import com.qf.data.user.entity.WxScoreDetails;
import com.qf.data.user.vo.input.WxScoreUpdateInput;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.Date;

/**
 * 红包表(RedEnvelopes)表服务实现类 - redis版本
 *
 * @author makejava
 * @since 2022-09-27 14:35:28
 */
@Service
@Slf4j
@Primary
public class RedEnvelopesServiceImpl2 extends ServiceImpl<RedEnvelopesDao, RedEnvelopes> implements RedEnvelopesService {

    @Autowired
    private WxUserFeign wxUserFeign;

    @Autowired
    private RedEnvelopesDetailsService redEnvelopesDetailsService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 创建红包的lua脚本
     */
    private String createRedLua = "--接收红包id\n" +
            "local redid = KEYS[1]\n" +
            "--接收红包的相关参数\n" +
            "local type = ARGV[1]\n" +
            "local score = ARGV[2]\n" +
            "local count = ARGV[3]\n" +
            "\n" +
            "--生成一个key\n" +
            "local key = 'red_envlopes_'..redid\n" +
            "\n" +
            "--判断当前红包是否存在\n" +
            "local result = redis.call('exists', key)\n" +
            "if result == 1 then\n" +
            "  -- 红包已经存在\n" +
            "  return -1\n" +
            "end\n" +
            "\n" +
            "--创建红包\n" +
            "redis.call('hset', key, 'type', type)\n" +
            "redis.call('hset', key, 'score', score)\n" +
            "redis.call('hset', key, 'count', count)\n" +
            "return 1";

    /**
     * 发送红包
     * @param redEvnlopesInput
     * @return
     */
    @Override
    @GetUser
    @GlobalTransactional
    public int sendRed(RedEvnlopesInput redEvnlopesInput) {
        //将入参对象 转换成 红包对象
        RedEnvelopes redEnvelopes = QfBeanUtils.copyBean(redEvnlopesInput, RedEnvelopes.class);
        //获取用户id
        Integer uid = UserUtils.getUser().getId();
        redEnvelopes.setUid(uid);
        //设置过期时间 - 24小时过期
        redEnvelopes.setTimeout(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000));

        //如果积分足够 扣减积分 -- 分布式事务
        Integer result = wxUserFeign.updateWxScore(new WxScoreUpdateInput().setType(0).setScore(redEnvelopes.getScount()).setUid(redEnvelopes.getUid())).getData();
        if (result <= 0) {
            throw new ServiceException(500, "积分不足，无法发送红包！");
        }

        //积分足够并且已经扣减
        this.save(redEnvelopes);
        //红包id
        Integer redId = redEnvelopes.getId();

        //生成用户的积分流水
        wxUserFeign.createScoreDetails(new WxScoreDetails()
                .setUid(uid)
                .setBusid(redId)
                .setTarget(0)
                .setSourceScore(0)
                .setActionScore(-redEnvelopes.getScount()));

        //通过lua脚本写入 红包信息到redis
        Long flag = redisTemplate.execute(new DefaultRedisScript<Long>(createRedLua, Long.class),
                Collections.singletonList(redId + ""),
                redEnvelopes.getType() + "",
                redEnvelopes.getScount() + "",
                redEnvelopes.getNumber() + "");

        if (flag != 1) {
            throw new ServiceException(500, "红包创建异常...");
        }

        return redId;
    }

    /**
     * 抢红包
     * @param redId
     * @return
     */
    @Override
//    @GetUser
    @KLock("'red-' + #redId")
    @GlobalTransactional
    public int robRed(Integer redId) {

        //获取抢红包的用户id
//        Integer uid = UserUtils.getUser().getId();
        Integer uid = (int)(Math.random() * 1000000);
        //根据红包id 查询红包信息
//        RedEnvelopes redEnvelopes = this.getById(redId);
        RedEnvelopesDto redEnvelopes = getBaseMapper().queryRedById(redId);
        log.debug("[rob red] 抢红包 - {}", redEnvelopes);

        //判断红包的状态
        if(redEnvelopes.getStatus() == 1){
            //红包已经抢完
            return -1;
        } else if(redEnvelopes.getStatus() == 2){
            //红包已经过期
            return -2;
        }

        //判断当前用户是否已经抢过
        Long count = redEnvelopesDetailsService.query()
                .eq("redid", redId)
                .eq("uid", uid).count();
        if (count > 0) {
            //当前红包已经抢过了
            return -3;
        }

        //抢到的积分
        Integer robScore = 0;

        //红包状态正常 还可以抢
        //剩余积分数 剩余可抢人数
        if(redEnvelopes.getType() == 0){
            //固定红包
            robScore = redEnvelopes.getHasScore() / redEnvelopes.getHasCount();
        } else {
            //随机红包 随机算法
            if(redEnvelopes.getHasCount() == 1){
                //剩余最后一人
                robScore = redEnvelopes.getHasScore();
            } else {
                //红包随机算法  二倍均值法  1 ~ 积分 / 剩余人数 * 2   10000  1~10000
                // 10000 10  1 -> 1 ~ 2000 -> 1000
                //           2 -> 1 ~ 2000 -> 1000
                robScore = (int)(Math.random() * (redEnvelopes.getHasScore() / redEnvelopes.getHasCount() * 2 - 1)) + 1;
            }
        }

        if(redEnvelopes.getHasCount() == 1){
            //修改红包状态
            RedEnvelopes red = QfBeanUtils.copyBean(redEnvelopes, RedEnvelopes.class);
            red.setStatus(1);
            this.updateById(red);
        }

        //添加红包明细记录
        RedEnvelopesDetails redEnvelopesDetails = new RedEnvelopesDetails()
                .setUid(uid)
                .setRedid(redId)
                .setType(0)//抢红包
                .setScore(robScore);//抢到的积分数
        redEnvelopesDetailsService.save(redEnvelopesDetails);

        //修改用户积分
//        wxUserFeign.updateWxScore(new WxScoreUpdateInput()
//                .setScore(robScore)
//                .setUid(uid)
//                .setType(1));//1 表示增加积分

        //生成用户积分流水
        wxUserFeign.createScoreDetails(new WxScoreDetails()
                .setUid(uid)
                .setBusid(redId)
                .setSourceScore(0)
                .setActionScore(robScore)
                .setTarget(1));//1 表示是通过抢红包获得的积分

        return robScore;
    }

    @Override
//    @GlobalTransactional
    public int robRed(Integer redId, Integer uid, Integer score) {
        //添加红包明细记录
        RedEnvelopesDetails redEnvelopesDetails = new RedEnvelopesDetails()
                .setUid(uid)
                .setRedid(redId)
                .setType(0)//抢红包
                .setScore(score);//抢到的积分数
        redEnvelopesDetailsService.save(redEnvelopesDetails);

        //修改用户积分
//        wxUserFeign.updateWxScore(new WxScoreUpdateInput()
//                .setScore(score)
//                .setUid(uid)
//                .setType(1));//1 表示增加积分

        //生成用户积分流水
        wxUserFeign.createScoreDetails(new WxScoreDetails()
                .setUid(uid)
                .setBusid(redId)
                .setSourceScore(0)
                .setActionScore(score)
                .setTarget(1));//1 表示是通过抢红包获得的积分
        return 1;
    }

    /**
     * 红包回退
     * @param redId
     * @return
     */
    @Override
    public int redBack(Integer redId) {
        return 0;
    }

    public static void main(String[] args) {
        Integer score = 500;
        Integer count = 10;

        int len = count;
        //抢到的金额
        int robScore = 0;

        int sum = 0;
        for (Integer i = 1; i <= len; i++) {
            //判断是否为最后一个人
            if(count == 1){
                robScore = score;
            } else {
                //二倍均值法 [0, 2)
                robScore = (int)(Math.random() * (score / count * 2 - 1)) + 1;
//                robScore = (int) (Math.random() * (score - (count - 1)) + 1);
            }

            System.out.println("第" + i + "个人抢到的金额：" + robScore);

            sum += robScore;

            score -= robScore;
            count--;
        }

        System.out.println("总和：" + sum);
    }
 }